﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpMoney.Library
{
	public struct Money
	{
		#region Variables

		private decimal _value;
		private Currency _currency;

		#endregion

		#region Properties

		public decimal Value
		{
			get
			{
				return _value;
			}
			set
			{
				_value = value;
			}
		}

		public Currency Currency
		{
			get
			{
				return _currency;
			}
			set
			{
				_currency = value;
			}
		}

		#endregion

		#region Constructors

		public Money( decimal value )
			: this( value, Currency.Default )
		{
		}

		public Money( decimal value, Currency currency )
		{
			this._value = value;
			this._currency = currency;
		}

		#endregion

		#region Convert

		public Money Convert( ICurrencyConverter converter, Currency convertTo )
		{
			if ( converter == null )
				throw new ArgumentNullException( "converter" );

			return converter.Convert( this, convertTo );
		}

		#endregion

		#region Base Class Overrides

		public override string ToString()
		{
			return string.Format(
				"{0}{1}",
				this.Currency,
				this.Value
			);
		}

		public string ToString( string format )
		{
			return string.Format(
				format,
				this.Currency,
				this.Value.ToString( format )
			);
		}

		public override bool Equals( object obj )
		{
			return base.Equals( obj );
		}

		public override int GetHashCode()
		{
			return base.GetHashCode();
		}

		#endregion

		#region Operator Overloads

		public static bool operator ==( Money a, Money b )
		{
			return a.Value == b.Value;
		}

		public static bool operator !=( Money a, Money b )
		{
			return a.Value != b.Value;
		}

		public static bool operator >( Money a, Money b )
		{
			return a.Value > b.Value;
		}

		public static bool operator <( Money a, Money b )
		{
			return a.Value < b.Value;
		}

		public static bool operator <=( Money a, Money b )
		{
			return a.Value <= b.Value;
		}

		public static bool operator >=( Money a, Money b )
		{
			return a.Value >= b.Value;
		}

		public static Money operator ++( Money a )
		{
			a.Value++;

			return a;
		}

		public static Money operator --( Money a )
		{
			a.Value--;

			return a;
		}

		public static Money operator +( Money a, Money b )
		{
			if ( a.Currency != b.Currency )
				throw new InvalidOperationException( "Cannot add money having different currencies." );

			Money c = new Money( a.Value + b.Value );

			return c;
		}

		public static Money operator -( Money a, Money b )
		{
			if ( a.Currency != b.Currency )
				throw new InvalidOperationException( "Cannot subtract money having different currencies." );

			Money c = new Money( a.Value - b.Value );

			return c;
		}

		public static Money operator *( Money a, Money b )
		{
			if ( a.Currency != b.Currency )
				throw new InvalidOperationException( "Cannot multiply money having different currencies." );

			Money c = new Money( a.Value * b.Value );

			return c;
		}

		public static Money operator /( Money a, Money b )
		{
			if ( a.Currency != b.Currency )
				throw new InvalidOperationException( "Cannot divide money having different currencies." );

			Money c = new Money( a.Value / b.Value );

			return c;
		}

		#endregion

		#region Overloaded Implicit Operators

		public static implicit operator Money( byte value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( int value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( long value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( sbyte value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( short value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( uint value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( ulong value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( ushort value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( decimal value )
		{
			return new Money( value );
		}

		public static implicit operator Money( double value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		public static implicit operator Money( float value )
		{
			return new Money( System.Convert.ToDecimal( value ) );
		}

		#endregion
	}
}
